using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Windows.Controls;
using Microsoft.PowerShell.Host.ISE;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.VersionControl.Client;
using Microsoft.Win32;

namespace Empanada
{
    public class EmpanadaPanePresenter
    {
        private ObjectModelRoot _hostObject;
        private Model _model;
        private ISEFileCollectionMonitor _filesColleMonitor;
        private EmpanadaWorkspace _workspace;
        private PowerShellTabSelectedFileMonitor _tabSelectedFileMonitor;

        public EmpanadaPanePresenter(INotifyPropertyChanged empanadaPane)
        {
            empanadaPane.PropertyChanged += PanePropertyChanged;
        }

        private RegistryKey GetConfigurationKey(bool writable)
        {
            const string path = @"Software\CodeAssassin\Empanada";
            Registry.CurrentUser.CreateSubKey(path);
            return Registry.CurrentUser.OpenSubKey(path, writable);
        }

        void PanePropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            var empanadaPane = (IAddOnToolHostObject) sender;
            if (empanadaPane.HostObject == null) return;
            
            _hostObject = empanadaPane.HostObject;

            _filesColleMonitor = new ISEFileCollectionMonitor(_hostObject.CurrentPowerShellTab.Files);
            _filesColleMonitor.FileModified += ISEFileModified;

            _tabSelectedFileMonitor = new PowerShellTabSelectedFileMonitor(_hostObject.CurrentPowerShellTab);
            _tabSelectedFileMonitor.SelectedFileChanged += SelectedFileChanged;

            _model = new Model
                         {
                             CheckOutFilesOnEdit = Convert.ToBoolean(GetConfigurationKey(false).GetValue("CheckOutFilesOnEdit", 0)),
                             GetLatestSelectedFileCommand = new DelegateCommand(GetLatestSelectedFile, false),
                             AddSelectedFileCommand = new DelegateCommand(AddSelectedFile, false),
                             CheckOutSelectedFileCommand = new DelegateCommand(CheckOutSelectedFile, false),
                             UndoSelectedFileCommand = new DelegateCommand(UndoSelectedFile, false),
                             CheckInSelectedFileCommand = new DelegateCommand(CheckInSelectedFile, false)
                             
                         };
            _model.PropertyChanged += ModelPropertyChanged;
            ((Control)sender).DataContext = _model;

            SelectedFileChanged(_hostObject.CurrentPowerShellTab.Files.SelectedFile);
        }

        private void GetLatestSelectedFile()
        {
            var selectedFile = _tabSelectedFileMonitor.SelectedFile;
            if (selectedFile == null || selectedFile.IsUntitled) return;

            if (!selectedFile.IsSaved)
            {
                // todo confirm to merge/lose changes
                return;
            }

            var localPath = selectedFile.FullPath;
            var isVersionControlled = IsVersionControlled(localPath);
            if (!isVersionControlled) return;

            var workspace = GetWorkspace(localPath);

            var status = workspace.Get(new[] {localPath}, VersionSpec.Latest, RecursionType.None, GetOptions.None);
            Debug.WriteLine("Get: {0}", status);

            _tabSelectedFileMonitor.SelectedFile = Reload(selectedFile);
        }

        private ISEFile Reload(ISEFile file)
        {
            var newPath = file.FullPath;
            var files = _hostObject.CurrentPowerShellTab.Files;
            files.Remove(file, true);
            var newFile = files.Add(newPath);
            return newFile;
        }

        private void CheckInSelectedFile()
        {
            // todo confirm, work items, check-in policies

            var selectedFile = _tabSelectedFileMonitor.SelectedFile;
            if (selectedFile == null || selectedFile.IsUntitled) return;

            var localPath = selectedFile.FullPath;
            var isVersionControlled = IsVersionControlled(localPath);
            if (!isVersionControlled) return;

            var workspace = GetWorkspace(localPath);
            var pendingChanges = workspace.GetPendingChanges(localPath);
            if (pendingChanges.Length == 0) return;

            var changeset = workspace.CheckIn(pendingChanges, _model.CheckInComment);
            Debug.WriteLine("CheckIn: {0}", changeset);
            _model.CheckInComment = string.Empty;

            SelectedFileChanged(selectedFile);
        }

        private void UndoSelectedFile()
        {
            // todo prompt for confirmation?

            var selectedFile = _tabSelectedFileMonitor.SelectedFile;
            if (selectedFile == null || selectedFile.IsUntitled) return;

            var localPath = selectedFile.FullPath;
            var isVersionControlled = IsVersionControlled(localPath);
            if (!isVersionControlled) return;

            var workspace = GetWorkspace(localPath);
            var pendingChanges = workspace.GetPendingChanges(localPath);
            var result = workspace.Undo(pendingChanges);
            Debug.WriteLine("Undo: {0}", result);

            SelectedFileChanged(selectedFile);
            //TODO refresh reverted file, consider renamed, added(ie deleted), merged, branched
        }

        private void CheckOutSelectedFile()
        {
            var selectedFile = _tabSelectedFileMonitor.SelectedFile;
            if (selectedFile == null || selectedFile.IsUntitled) return;

            var localPath = selectedFile.FullPath;
            var isVersionControlled = IsVersionControlled(localPath);
            if (!isVersionControlled) return;

            var workspace = GetWorkspace(localPath);
            var result = workspace.PendEdit(localPath);
            Debug.WriteLine("PendEdit: {0}", result);

            SelectedFileChanged(selectedFile);
        }

        private void ModelPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            var model = (Model) sender;
            if (e.PropertyName.Equals("CheckOutFilesOnEdit", StringComparison.InvariantCultureIgnoreCase))
            {
                GetConfigurationKey(true).SetValue("CheckOutFilesOnEdit", model.CheckOutFilesOnEdit ? 1 : 0, RegistryValueKind.DWord);
            }
        }

        void ISEFileModified(object sender, ISEFileEventArgs e)
        {
            if (_model.CheckOutFilesOnEdit)
            {
                CheckoutFile(e.File.FullPath);
            }
        }

        void AddSelectedFile()
        {
            var selectedFile = _tabSelectedFileMonitor.SelectedFile;
            if (selectedFile == null || selectedFile.IsUntitled) return;

            var localPath = selectedFile.FullPath;
            var isVersionControlled = IsVersionControlled(localPath);
            if (isVersionControlled) return;

            var workspace = GetWorkspace(localPath);
            var result = workspace.PendAdd(localPath);
            Debug.WriteLine("PendAdd: {0}", result);

            SelectedFileChanged(selectedFile);
        }

        private void SelectedFileChanged(object sender, ISEFileEventArgs e)
        {
            SelectedFileChanged(e.File);
        }

        private void SelectedFileChanged(ISEFile file)
        {
            var isFileOnDisk = file != null && !file.IsUntitled;
            var localPath = isFileOnDisk ? file.FullPath : string.Empty;
            var workspace = isFileOnDisk ? GetWorkspace(localPath) : null;
            var hasWorkspace = workspace != null;
            WorkspaceChanged(workspace);

            var empanadaFile = UnmappedFile.Instance;
            if (hasWorkspace)
            {
                empanadaFile = WorkspaceMappedFile.Instance;
                if (IsVersionControlled(localPath))
                {
                    empanadaFile = new VersionControlledFile(localPath, workspace);
                }
            }

            _model.SelectedServerPath = empanadaFile.ServerPath;
            ((DelegateCommand)_model.GetLatestSelectedFileCommand).SetCanExecute(empanadaFile.CanGetLatest);
            ((DelegateCommand)_model.AddSelectedFileCommand).SetCanExecute(empanadaFile.CanAdd);
            ((DelegateCommand)_model.CheckInSelectedFileCommand).SetCanExecute(empanadaFile.CanCheckInOrUndo);
            ((DelegateCommand)_model.CheckOutSelectedFileCommand).SetCanExecute(empanadaFile.CanCheckOut);
            ((DelegateCommand)_model.UndoSelectedFileCommand).SetCanExecute(empanadaFile.CanCheckInOrUndo);
        }

        private bool IsVersionControlled(string path)
        {
            var workspace = GetWorkspace(path);
            if (workspace.VersionControlServer.ServerItemExists(path, ItemType.File)) return true;
            return workspace.GetPendingChanges(path).Any();
        }

        private void WorkspaceChanged(Workspace workspace)
        {
            if (_workspace != null)
            {
                if (_workspace.Workspace == workspace) return;
                _workspace.Pause();
            } 

            if (workspace == null)
            {
                _workspace = null;
                _model.PendingChanges.Clear();
                _model.CollectionUri = null;
                _model.WorkspaceDisplayName = string.Empty;
                return;
            }
            
            _workspace = new EmpanadaWorkspace(workspace, _model.PendingChanges);

            _model.CollectionUri = workspace.VersionControlServer.TeamProjectCollection.Uri;
            _model.WorkspaceDisplayName = workspace.DisplayName;

        }


        private void CheckoutFile(string path)
        {
            var workspace = GetWorkspace(path);
            if (workspace != null)
            {
                var result = workspace.PendEdit(path);
                Debug.WriteLine("PendEdit('{0}') returned {1}", path, result);
            }
        }

        private Workspace GetWorkspace(string path)
        {
            if (!Workstation.Current.IsMapped(path))
            {
                return null;
            }

            var workspaceInfo = Workstation.Current.GetLocalWorkspaceInfo(path);
            var collection = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(workspaceInfo.ServerUri);
            collection.EnsureAuthenticated();
            return workspaceInfo.GetWorkspace(collection);
        }
   
    }
}